package com.cmc.cloud.cmclink.doc.service.impl.si;

import cn.hutool.core.collection.CollectionUtil;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.api.document.rep.BlnoFobReqDto;
import com.cmc.cloud.cmclink.doc.api.document.rep.BlnoFobRespDto;
import com.cmc.cloud.cmclink.doc.api.document.rep.BlnoNumericalContainerRespDto;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.BlnoLocalChargeMapper;
import com.cmc.cloud.cmclink.doc.mapper.BlnoLocalCostMapper;
import com.cmc.cloud.cmclink.doc.mapper.BlnoMapper;
import com.cmc.cloud.cmclink.doc.mapper.BlnoVslvoyMapper;
import com.cmc.cloud.cmclink.doc.rpcservice.CustomerRpcService;
import com.cmc.cloud.cmclink.doc.service.BlnoCntrService;
import com.cmc.cloud.cmclink.doc.service.si.BlnoCargoService;
import com.cmc.cloud.cmclink.doc.service.si.BlnoScnService;
import com.cmc.cloud.cmclink.doc.service.si.BlnoVslvoyService;
import com.cmc.cloud.cmclink.doc.util.CostChargeUtil;
import com.cmc.cloud.cmclink.doc.vo.blnovo.fob.BlnoFobVO;
import com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 提单主信息 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
@Slf4j
public class BlnoQueryServiceImpl {
    @Resource
    private BlnoMapper blnoMapper;
    @Resource
    private BlnoVslvoyMapper blnoVslvoyMapper;
    @Resource
    private BlnoVslvoyService blnoVslvoyService;
    @Resource
    private BlnoScnService blnoScnService;
    @Resource
    private BlnoCargoService blnoCargoService;
    @Resource
    private BlnoCntrService blnoCntrService;
    @Resource
    private CustomerRpcService nameTransferService;
    @Resource
    private BlnoFreightServiceImpl blnoFreightService;
    @Resource
    private BlnoLocalChargeMapper blnoLocalChargeMapper;
    @Resource
    private BlnoLocalCostMapper blnoLocalCostMapper;
    @Resource
    private CostChargeUtil costChargeUtil;

    public BlnoFobReqDto queryFobParam(BlnoFobReqDto reqVo) {
        List<BlnoVslvoyDO> queryList = blnoVslvoyMapper.queryFobVslvoy(reqVo);
        if (CollectionUtil.isNotEmpty(queryList)) {
            List<Long> blnoIds = queryList.stream().map(e -> e.getBlnoId()).collect(Collectors.toList());
            List<BlnoDO> blnoList = blnoMapper.selectBatchIds(blnoIds);
            List<BlnoVslvoyDO> vslvoyList = blnoVslvoyService.getBlnoVslvoyBySiIdList(blnoIds);
            List<BlnoCargoDO> blnoCargoList = blnoCargoService.getBlnoCargoByBlnoIds(blnoIds);
            List<BlnoCntrDO> blnoCntrList = blnoCntrService.getBlnoCntrByBlnoIds(blnoIds);
            List<BlnoFreightDO> blnoFreightList = blnoFreightService.getBlnoFreightByBlnoIdList(blnoIds);
            Set<String> cargoTypeList = blnoCargoList.stream().map(e -> e.getCargoType()).collect(Collectors.toSet());
            Set<String> cntrTypeList = blnoCntrList.stream().map(e -> e.getCntrType()).collect(Collectors.toSet());
            Set<String> paymentTermList = new HashSet<>(16);
            Set<String> chargeCodeList = new HashSet<>(16);
            for (BlnoFreightDO blnoFreight : blnoFreightList) {
                paymentTermList.add(blnoFreight.getPayment());
                chargeCodeList.add(blnoFreight.getChargeCode());
            }
            Map<String, String> bookingPartyMap = new HashMap<>(16);
            List<String> bookingPartyList = new ArrayList<>(16);
            Map<String, String> splitCombineStatusMap = new HashMap<>(16);
            Set<String> porList = new HashSet<>(16);
            Set<String> delList = new HashSet<>(16);
            Set<String> cntrStatusList = new HashSet<>(16);
            Set<String> spaceOwnerList = new HashSet<>(16);
            Set<String> cntrOwnerList = new HashSet<>(16);
            Map<String, String> topAgentMap = new HashMap<>(16);
            List<String> topAgentList = new ArrayList<>(16);
            for (BlnoDO blno : blnoList) {
                bookingPartyList.add(blno.getBkpCode());
                splitCombineStatusMap.put(blno.getSplitCombineType(), SiSplitCombineEnum.getLabelByValue(blno.getSplitCombineType()));
                porList.add(blno.getPorCode());
                delList.add(blno.getDelCode());
                spaceOwnerList.add(blno.getSpaceOwner());
                cntrOwnerList.add(blno.getCntrOwner());
                cntrStatusList.add(blno.getCntrStatus());
                topAgentList.add(blno.getTopAgentCode());
            }
            bookingPartyMap = nameTransferService.getCompanyLocalNameMap(bookingPartyList);
            if (CollectionUtil.isNotEmpty(topAgentList)) {
                Map<String, String> topAgentMapTemp = nameTransferService.getCompanyLocalNameMap(bookingPartyList);
                if (CollectionUtil.isNotEmpty(topAgentMapTemp)) {
                    topAgentMap.putAll(topAgentMapTemp);
                }
            }
            Set<String> polList = new HashSet<>(16);
            Set<String> podList = new HashSet<>(16);
            for (BlnoVslvoyDO vs : vslvoyList) {
                polList.add(vs.getPolCode());
                podList.add(vs.getPodCode());
            }
            reqVo.setCargoTypeList(cargoTypeList);
            reqVo.setCntrTypeList(cntrTypeList);
            reqVo.setPaymentTermList(paymentTermList);
            reqVo.setChargeCodeList(chargeCodeList);
            reqVo.setBookingPartyMap(bookingPartyMap);
            reqVo.setBookingPartyList(bookingPartyList);
            reqVo.setSplitCombineStatusMap(splitCombineStatusMap);
            reqVo.setSplitCombineStatusList(splitCombineStatusMap.keySet().stream().collect(Collectors.toList()));
            reqVo.setPorList(porList);
            reqVo.setDelList(delList);
            reqVo.setCntrStatusList(cntrStatusList);
            reqVo.setSpaceOwnerList(spaceOwnerList);
            reqVo.setCntrOwnerList(cntrOwnerList);
            reqVo.setTopAgentMap(topAgentMap);
            reqVo.setTopAgentList(topAgentList);
            reqVo.setPolList(polList);
            reqVo.setPodList(podList);
        }
        return reqVo;
    }

    public BlnoFobRespDto queryFobInfo(BlnoFobReqDto reqVo) {
        List<BlnoVslvoyDO> queryList = blnoVslvoyMapper.queryFobVslvoy(reqVo);
        List<BlnoFobVO> fobList = new ArrayList<>(16);
        if (CollectionUtil.isNotEmpty(queryList)) {
            List<Long> blnoIds = queryList.stream().map(e -> e.getBlnoId()).collect(Collectors.toList());
            Map<Long, List<BlnoVslvoyDO>> vslvoyListMap = blnoVslvoyService.getBlnoVslvoyBySiIdList(blnoIds).stream().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
            Map<Long, BlnoVslvoyDO> vslvoyMap = queryList.stream().collect(Collectors.toMap(BlnoVslvoyDO::getBlnoId, e -> e));
            Map<Long, BlnoDO> blnoMap = blnoMapper.selectBatchIds(blnoIds).stream().collect(Collectors.toMap(BlnoDO::getId, e -> e));
            Map<Long, BlnoCargoDO> blnoCargoMap = blnoCargoService.getBlnoCargoByBlnoIds(blnoIds).stream().collect(Collectors.toMap(BlnoCargoDO::getBlnoId, e -> e));
            Map<Long, List<BlnoScnDO>> blnoScnMap = blnoScnService.getBlnoScnByBlnoIdList(blnoIds).stream().collect(Collectors.groupingBy(BlnoScnDO::getBlnoId));
            Map<Long, List<BlnoCntrDO>> blnoCntrMap = blnoCntrService.getBlnoCntrByBlnoIds(blnoIds).stream().collect(Collectors.groupingBy(BlnoCntrDO::getBlnoId));
            Map<Long, List<BlnoFreightDO>> blnoFreightMap = blnoFreightService.getBlnoFreightByBlnoIdList(blnoIds).stream().collect(Collectors.groupingBy(BlnoFreightDO::getBlnoId));
            Map<Long, List<BlnoLocalChargeDO>> blnoLocalChargeMap = new HashMap<>(16);
            if (reqVo.isIncludeLocalCharge()) {
                blnoLocalChargeMap = getBlnoChargeByBlnoIds(blnoIds, reqVo.getExImType()).stream().collect(Collectors.groupingBy(BlnoLocalChargeDO::getBlnoId));
            }
            for (Map.Entry<Long, BlnoDO> blnoEntry : blnoMap.entrySet()) {
                BlnoFobVO fob = new BlnoFobVO();
                fob.setBlno(blnoEntry.getValue());
                fob.setBlnoCargo(blnoCargoMap.get(blnoEntry.getKey()));
                fob.setBlnoVslvoy(vslvoyMap.get(blnoEntry.getKey()));
                fob.setBlnoVslvoyList(vslvoyListMap.get(blnoEntry.getKey()));
                fob.setBlnoScnList(blnoScnMap.get(blnoEntry.getKey()));
                fob.setBlnoCntrList(blnoCntrMap.get(blnoEntry.getKey()));
                fob.setBlnoFreightList(blnoFreightMap.get(blnoEntry.getKey()));
                fob.setBlnoLocalChargeList(blnoLocalChargeMap.get(blnoEntry.getKey()));
                if (CollectionUtil.isEmpty(reqVo.getCargoTypeList()) || reqVo.getCargoTypeList().contains(fob.getBlnoCargo().getCargoType()) == false) {
                    continue;
                }
                if (CollectionUtil.isNotEmpty(reqVo.getCntrTypeList())) {
                    //只要存在一个就可以
                    List<BlnoCntrDO> blnoCntrList = fob.getBlnoCntrList().stream().filter(e -> reqVo.getCntrTypeList().contains(e.getCntrType())).collect(Collectors.toList());
                    if (CollectionUtil.isEmpty(blnoCntrList)) {
                        continue;
                    }
                }
                if (CollectionUtil.isNotEmpty(fob.getBlnoFreightList())) {
                    Optional<BlnoFreightDO> frtFreight = fob.getBlnoFreightList().stream().filter(e -> "FRT".equals(e.getChargeCode())).findFirst();
                    if (frtFreight.isPresent()) {
                        fob.setFrtFreight(frtFreight.get());
                    }
                    if (CollectionUtil.isNotEmpty(reqVo.getPaymentTermList())) {
                        //简单过滤
                        List<BlnoFreightDO> blnoFreightList = fob.getBlnoFreightList().stream().filter(e -> reqVo.getPaymentTermList().contains(e.getPayment())).collect(Collectors.toList());
                        fob.setBlnoFreightList(blnoFreightList);
                    } else {
                        continue;
                    }
                    if (CollectionUtil.isNotEmpty(reqVo.getChargeCodeList())) {
                        //简单过滤 再来一次
                        List<BlnoFreightDO> blnoFreightList = fob.getBlnoFreightList().stream().filter(e -> reqVo.getChargeCodeList().contains(e.getChargeCode())).collect(Collectors.toList());
                        fob.setBlnoFreightList(blnoFreightList);
                    } else {
                        continue;
                    }
                }
                if (CollectionUtil.isEmpty(reqVo.getBookingPartyList()) || reqVo.getBookingPartyList().contains(fob.getBlno().getBkpCode()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getSplitCombineStatusList()) || reqVo.getSplitCombineStatusList().contains(fob.getBlno().getSplitCombineType()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getPorList()) || reqVo.getPorList().contains(fob.getBlno().getPorCode()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getDelList()) || reqVo.getDelList().contains(fob.getBlno().getDelCode()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getSpaceOwnerList()) || reqVo.getSpaceOwnerList().contains(fob.getBlno().getSpaceOwner()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getCntrOwnerList()) || reqVo.getCntrOwnerList().contains(fob.getBlno().getCntrOwner()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getCntrStatusList()) || reqVo.getCntrStatusList().contains(fob.getBlno().getCntrStatus()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getTopAgentList()) || reqVo.getTopAgentList().contains(fob.getBlno().getTopAgentCode()) == false) {
                    continue;
                }

                if (CollectionUtil.isNotEmpty(reqVo.getPolList())) {
                    //只要存在一个就可以
                    List<BlnoVslvoyDO> blnoVslvoyList = fob.getBlnoVslvoyList().stream().filter(e -> reqVo.getPolList().contains(e.getPolCode())).collect(Collectors.toList());
                    if (CollectionUtil.isEmpty(blnoVslvoyList)) {
                        continue;
                    }
                }
                if (CollectionUtil.isNotEmpty(reqVo.getPodList())) {
                    //只要存在一个就可以
                    List<BlnoVslvoyDO> blnoVslvoyList = fob.getBlnoVslvoyList().stream().filter(e -> reqVo.getPodList().contains(e.getPodCode())).collect(Collectors.toList());
                    if (CollectionUtil.isEmpty(blnoVslvoyList)) {
                        continue;
                    }
                }
                fobList.add(fob);
            }
        }
        return toSum(fobList, reqVo.getExImType());
    }

    public BlnoFobRespDto toSum(List<BlnoFobVO> fobList, String exImType) {
        BlnoFobRespDto fobResult = new BlnoFobRespDto();
        if (CollectionUtil.isEmpty(fobList)) {
            return fobResult;
        }
        // 统计数据
        Map<String, BigDecimal> freightTotalMap = new HashMap<>(16);
        Map<String, Integer> cntrTotalMap = new HashMap<>(16);
        List<BlnoFobRespDto.DangerousInfo> dangerousInfoList = new ArrayList<>(16);
        fobResult.setInfoList(new ArrayList<>(16));
        fobResult.setDangerousInfoList(dangerousInfoList);
        fobResult.setVesselCode(fobList.get(0).getBlnoVslvoy().getVesselCode());
        if (ExImTypeEnum.EX.getValue().equals(exImType)) {
            fobResult.setLaneCode(fobList.get(0).getBlnoVslvoy().getLaneCodeEx());
            fobResult.setVoyage(fobList.get(0).getBlnoVslvoy().getVoyageEx());
            fobResult.setPodPol(fobList.get(0).getBlnoVslvoy().getPolCode());
            fobResult.setEtdEta(getVesselVoyTerminal(fobResult.getLaneCode(), fobResult.getVoyage(), fobResult.getVesselCode(), fobResult.getPodPol(), ExImTypeEnum.EX.getValue()));
        } else {
            fobResult.setLaneCode(fobList.get(0).getBlnoVslvoy().getLaneCodeIm());
            fobResult.setVoyage(fobList.get(0).getBlnoVslvoy().getVoyageIm());
            fobResult.setPodPol(fobList.get(0).getBlnoVslvoy().getPodCode());
            fobResult.setEtdEta(getVesselVoyTerminal(fobResult.getLaneCode(), fobResult.getVoyage(), fobResult.getVesselCode(), fobResult.getPodPol(), ExImTypeEnum.IM.getValue()));
        }

        for (BlnoFobVO fob : fobList) {
            BlnoFobRespDto.BaseInfo baseInfo = new BlnoFobRespDto.BaseInfo();
            fobResult.getInfoList().add(baseInfo);
            baseInfo.setBlNo(fob.getBlno().getBlNo());
            baseInfo.setPodPol(ExImTypeEnum.EX.getValue().equals(exImType) ? fob.getBlno().getPolCode() : fob.getBlno().getPodCode());
            baseInfo.setPayment(fob.getFrtFreight() != null ? fob.getFrtFreight().getPayment() : null);
            //箱状态+'/'+箱型+箱尺寸+"'"+箱量
            if (CollectionUtil.isNotEmpty(fob.getBlnoCntrList())) {
                List<String> strList = new ArrayList<>();
                for (BlnoCntrDO blnoCntrDO : fob.getBlnoCntrList()) {
                    String key = blnoCntrDO.getCntrSize() + "_" + blnoCntrDO.getCntrType();
                    Integer sum = cntrTotalMap.get(key);
                    if (sum == null) {
                        cntrTotalMap.put(key, blnoCntrDO.getQuantity());
                    } else {
                        cntrTotalMap.put(key, sum + blnoCntrDO.getQuantity());
                    }
                    strList.add(fob.getBlno().getCntrStatus() + "/" + blnoCntrDO.getCntrType() + blnoCntrDO.getCntrSize() + "'" + blnoCntrDO.getQuantity());
                }
                baseInfo.setVolume(StringUtils.join(strList, ";"));
            }
            //DG：危险品信息 IMOCLASS+"/"+UNNO。
            if (fob.getBlnoCargo() != null) {
                BlnoCargoDO blnoCargo = fob.getBlnoCargo();
                String checkSpecial = checkSpecialCargo(blnoCargo.getCargoType(), blnoCargo.getCargoSubType());
                if (StringUtils.isNotBlank(checkSpecial)) {
                    baseInfo.setDg(checkSpecial + " " + blnoCargo.getHsCode() + " " + fob.getBlnoCargo().getImoClass() + "/" + fob.getBlnoCargo().getUnNo());
                    BlnoFobRespDto.DangerousInfo dg = new BlnoFobRespDto.DangerousInfo();
                    dg.setBlNo(fob.getBlno().getBlNo());
                    dg.setPodPol(baseInfo.getPodPol());
                    dg.setContainer(baseInfo.getVolume());
                    dg.setDg(baseInfo.getDg());
                    dangerousInfoList.add(dg);
                }
            }
            //费用代码+金额+币种+付费方式
            List<String> ratePerCntrList = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(fob.getBlnoFreightList())) {
                for (BlnoFreightDO freightDO : fob.getBlnoFreightList()) {
                    String key = freightDO.getPayment() + "_" + freightDO.getCurrency() + "_" + freightDO.getChargeCode();
                    BigDecimal sum = freightTotalMap.get(key);
                    if (sum == null) {
                        freightTotalMap.put(key, freightDO.getAmount());
                    } else {
                        freightTotalMap.put(key, sum.add(freightDO.getAmount()));
                    }
                    ratePerCntrList.add(freightDO.getChargeCode() + String.format("%10s", freightDO.getAmount().toString()) + " " + freightDO.getCurrency() + " " + freightDO.getPayment());
                }
            }
            if (CollectionUtil.isNotEmpty(fob.getBlnoLocalChargeList())) {
                for (BlnoLocalChargeDO localDO : fob.getBlnoLocalChargeList()) {
                    String key = localDO.getPayment() + "_" + localDO.getCurrency() + "_" + localDO.getChargeCode();
                    BigDecimal sum = freightTotalMap.get(key);
                    if (sum == null) {
                        freightTotalMap.put(key, localDO.getAmount());
                    } else {
                        freightTotalMap.put(key, sum.add(localDO.getAmount()));
                    }
                    ratePerCntrList.add(localDO.getChargeCode() + String.format("%10s", localDO.getAmount().toString()) + " " + localDO.getCurrency() + " " + localDO.getPayment());
                }
            }
            if (CollectionUtil.isNotEmpty(ratePerCntrList)) {
                baseInfo.setRatePerCntr(StringUtils.join(ratePerCntrList, "\n"));
            }

            List<String> partyList = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(fob.getBlnoScnList())) {
                for (BlnoScnDO scn : fob.getBlnoScnList()) {
                    partyList.add(scn.getPartyTypeCode().toUpperCase() + " " + scn.getCompanyName() + " "
                            + scn.getCompanyAddress() + " " + scn.getCity() + " " + scn.getZipCode() + " "
                            + scn.getProvince() + " " + scn.getCountryCode() + " " + scn.getTelephone() + " " + scn.getFax());
                }
                baseInfo.setParty(StringUtils.join(partyList, "\n"));
            }
        }

        if (CollectionUtil.isNotEmpty(freightTotalMap)) {
            List<BlnoFobRespDto.FreightTotal> freightTotalList = new ArrayList<>(16);
            for (Map.Entry<String, BigDecimal> f : freightTotalMap.entrySet()) {
                String[] v = f.getKey().split("_");
                BlnoFobRespDto.FreightTotal total = new BlnoFobRespDto.FreightTotal();
                total.setPayment(v[0]);
                total.setCur(v[1]);
                total.setItem(v[2]);
                total.setAmount(f.getValue());
                freightTotalList.add(total);
            }
            freightTotalList = freightTotalList.stream().sorted(
                    Comparator.comparing(BlnoFobRespDto.FreightTotal::getPayment)
                            .thenComparing(BlnoFobRespDto.FreightTotal::getCur)
                            .thenComparing(BlnoFobRespDto.FreightTotal::getItem, Comparator.reverseOrder())).collect(Collectors.toList());
            fobResult.setFreightTotalList(freightTotalList);
        }
        if (CollectionUtil.isNotEmpty(cntrTotalMap)) {
            List<BlnoFobRespDto.ContainerTotal> cntrTotalList = new ArrayList<>(16);
            for (Map.Entry<String, Integer> f : cntrTotalMap.entrySet()) {
                String[] v = f.getKey().split("_");
                BlnoFobRespDto.ContainerTotal total = new BlnoFobRespDto.ContainerTotal();
                total.setSize(v[0]);
                total.setType(v[1]);
                total.setQty(f.getValue());
                cntrTotalList.add(total);
            }
            cntrTotalList = cntrTotalList.stream().sorted(
                    Comparator.comparing(BlnoFobRespDto.ContainerTotal::getSize)
                            .thenComparing(BlnoFobRespDto.ContainerTotal::getType)).collect(Collectors.toList());
            fobResult.setContainerTotalList(cntrTotalList);
        }
        return fobResult;
    }

    public String getVesselVoyTerminal(String laneCode, String voyage, String vessel, String portCode, String exImType) {
        Map<String, VesselVoyRespDTO> terminalMap = costChargeUtil.getVesselVoyMap(laneCode, voyage, vessel);
        String result = null;
        if (terminalMap != null) {
            VesselVoyRespDTO voyPol = terminalMap.get(portCode);
            if (voyPol != null) {
                if (ExImTypeEnum.EX.getValue().equals(exImType)) {
                    result = voyPol.getAtd();
                    // etd未维护，取默认值 ptd
                    if (StringUtils.isEmpty(result)) {
                        result = voyPol.getPtd();
                    }
                } else {
                    result = voyPol.getAta();
                    // eta未维护，取默认值 pta
                    if (StringUtils.isEmpty(result)) {
                        result = voyPol.getPta();
                    }
                }
            }
        }
        return result;
    }

    public static String checkSpecialCargo(String cargoType, String subCargoType) {
        if (CargoTypeEnum.AK.getValue().equals(cargoType) || CargoTypeEnum.RD.getValue().equals(cargoType)
                || CargoTypeEnum.DG.getValue().equals(cargoType)) {
            return cargoType;
        }

        if (CargoTypeEnum.DRY.getValue().equals(cargoType) &&
                (SubCargoTypeEnum.FLEXI.getValue().equals(subCargoType) || SubCargoTypeEnum.ODO.getValue().equals(subCargoType))) {
            return cargoType + " " + subCargoType;
        }
        return null;
    }

    public List<BlnoLocalChargeDO> getBlnoChargeByBlnoIds(Collection<Long> blnoIds, String imExType) {
        LambdaQueryWrapperX<BlnoLocalChargeDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(BlnoLocalChargeDO::getBlnoId, blnoIds);
        queryWrapperX.eq(BlnoLocalChargeDO::getImExType, imExType);
        return blnoLocalChargeMapper.selectList(queryWrapperX);
    }

    public BlnoNumericalContainerRespDto queryNumericalContainerListInfo(BlnoFobReqDto reqVo) {
        List<BlnoVslvoyDO> queryList = blnoVslvoyMapper.queryFobVslvoy(reqVo);
        List<BlnoFobVO> fobList = new ArrayList<>(16);
        if (CollectionUtil.isNotEmpty(queryList)) {
            List<Long> blnoIds = queryList.stream().map(e -> e.getBlnoId()).collect(Collectors.toList());
            Map<Long, List<BlnoVslvoyDO>> vslvoyListMap = blnoVslvoyService.getBlnoVslvoyBySiIdList(blnoIds).stream().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
            Map<Long, BlnoVslvoyDO> vslvoyMap = queryList.stream().collect(Collectors.toMap(BlnoVslvoyDO::getBlnoId, e -> e));
            Map<Long, BlnoDO> blnoMap = blnoMapper.selectBatchIds(blnoIds).stream().collect(Collectors.toMap(BlnoDO::getId, e -> e));
            Map<Long, BlnoCargoDO> blnoCargoMap = blnoCargoService.getBlnoCargoByBlnoIds(blnoIds).stream().collect(Collectors.toMap(BlnoCargoDO::getBlnoId, e -> e));
            Map<Long, List<BlnoCntrDO>> blnoCntrMap = blnoCntrService.getBlnoCntrByBlnoIds(blnoIds).stream().collect(Collectors.groupingBy(BlnoCntrDO::getBlnoId));
            for (Map.Entry<Long, BlnoDO> blnoEntry : blnoMap.entrySet()) {
                BlnoFobVO fob = new BlnoFobVO();
                fob.setBlno(blnoEntry.getValue());
                fob.setBlnoCargo(blnoCargoMap.get(blnoEntry.getKey()));
                fob.setBlnoVslvoyList(vslvoyListMap.get(blnoEntry.getKey()));
                fob.setBlnoVslvoy(vslvoyMap.get(blnoEntry.getKey()));
                fob.setBlnoCntrList(blnoCntrMap.get(blnoEntry.getKey()));
                if (CollectionUtil.isEmpty(reqVo.getCargoTypeList()) || reqVo.getCargoTypeList().contains(fob.getBlnoCargo().getCargoType()) == false) {
                    continue;
                }
                if (CollectionUtil.isNotEmpty(reqVo.getCntrTypeList())) {
                    //只要存在一个就可以
                    List<BlnoCntrDO> blnoCntrList = fob.getBlnoCntrList().stream().filter(e -> reqVo.getCntrTypeList().contains(e.getCntrType())).collect(Collectors.toList());
                    if (CollectionUtil.isEmpty(blnoCntrList)) {
                        continue;
                    }
                }
                if (CollectionUtil.isEmpty(reqVo.getBookingPartyList()) || reqVo.getBookingPartyList().contains(fob.getBlno().getBkpCode()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getSplitCombineStatusList()) || reqVo.getSplitCombineStatusList().contains(fob.getBlno().getSplitCombineType()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getPorList()) || reqVo.getPorList().contains(fob.getBlno().getPorCode()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getDelList()) || reqVo.getDelList().contains(fob.getBlno().getDelCode()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getSpaceOwnerList()) || reqVo.getSpaceOwnerList().contains(fob.getBlno().getSpaceOwner()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getCntrOwnerList()) || reqVo.getCntrOwnerList().contains(fob.getBlno().getCntrOwner()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getCntrStatusList()) || reqVo.getCntrStatusList().contains(fob.getBlno().getCntrStatus()) == false) {
                    continue;
                }
                if (CollectionUtil.isEmpty(reqVo.getTopAgentList()) || reqVo.getTopAgentList().contains(fob.getBlno().getTopAgentCode()) == false) {
                    continue;
                }
                if (CollectionUtil.isNotEmpty(reqVo.getPolList())) {
                    //只要存在一个就可以
                    List<BlnoVslvoyDO> blnoVslvoyList = fob.getBlnoVslvoyList().stream().filter(e -> reqVo.getPolList().contains(e.getPolCode())).collect(Collectors.toList());
                    if (CollectionUtil.isEmpty(blnoVslvoyList)) {
                        continue;
                    }
                }
                if (CollectionUtil.isNotEmpty(reqVo.getPodList())) {
                    //只要存在一个就可以
                    List<BlnoVslvoyDO> blnoVslvoyList = fob.getBlnoVslvoyList().stream().filter(e -> reqVo.getPodList().contains(e.getPodCode())).collect(Collectors.toList());
                    if (CollectionUtil.isEmpty(blnoVslvoyList)) {
                        continue;
                    }
                }
                fobList.add(fob);
            }
        }
        return toContainerSum(fobList, reqVo.getExImType());
    }

    public BlnoNumericalContainerRespDto toContainerSum(List<BlnoFobVO> fobList, String exImType) {
        BlnoNumericalContainerRespDto fobResult = new BlnoNumericalContainerRespDto();
        if (CollectionUtil.isEmpty(fobList)) {
            return fobResult;
        }
        // 统计数据
        Set<String> portCodeSet = new HashSet(16);
        fobResult.setVesselCode(fobList.get(0).getBlnoVslvoy().getVesselCode());
        if (ExImTypeEnum.EX.getValue().equals(exImType)) {
            fobResult.setLaneCode(fobList.get(0).getBlnoVslvoy().getLaneCodeEx());
            fobResult.setVoyage(fobList.get(0).getBlnoVslvoy().getVoyageEx());
            fobResult.setPolCode(fobList.get(0).getBlnoVslvoy().getPolCode());
            fobResult.setEtdEta(getVesselVoyTerminal(fobResult.getLaneCode(), fobResult.getVoyage(), fobResult.getVesselCode(), fobResult.getPolCode(), ExImTypeEnum.EX.getValue()));
        } else {
            fobResult.setLaneCode(fobList.get(0).getBlnoVslvoy().getLaneCodeIm());
            fobResult.setVoyage(fobList.get(0).getBlnoVslvoy().getVoyageIm());
            fobResult.setPodCode(fobList.get(0).getBlnoVslvoy().getPodCode());
            fobResult.setEtdEta(getVesselVoyTerminal(fobResult.getLaneCode(), fobResult.getVoyage(), fobResult.getVesselCode(), fobResult.getPodCode(), ExImTypeEnum.IM.getValue()));
        }
        Map<String, List<BlnoNumericalContainerRespDto.BaseInfo>> listMap = new LinkedHashMap<>(16);
        for (BlnoFobVO fob : fobList) {
            String portCode = "";
            if (ExImTypeEnum.EX.getValue().equals(exImType)) {
                portCode = fob.getBlno().getPodCode();
            } else {
                portCode = fob.getBlno().getPolCode();
            }
            portCodeSet.add(portCode);
            List<BlnoNumericalContainerRespDto.BaseInfo> baseList = listMap.get(portCode);
            if (CollectionUtil.isEmpty(baseList)) {
                baseList = new ArrayList<>(16);
                listMap.put(portCode, baseList);
            }
            for (BlnoCntrDO cntr : fob.getBlnoCntrList()) {
                BlnoNumericalContainerRespDto.BaseInfo baseInfo = new BlnoNumericalContainerRespDto.BaseInfo();
                baseInfo.setCntrNo(cntr.getCntrNo());
                baseInfo.setSealNo(cntr.getSealNo());
                baseInfo.setCntrSize(cntr.getCntrSize());
                baseInfo.setCntrType(cntr.getCntrType());
                baseInfo.setBlNo(fob.getBlno().getBlNo());
                baseInfo.setNoofpkgs(cntr.getQuantity() + " " + fob.getBlnoCargo().getPackingGroup());
                baseInfo.setContents(fob.getBlnoCargo().getCommodityEn());
                baseInfo.setNetWt(cntr.getGrossWeight());
                baseInfo.setCntrTare(cntr.getCntrTare() != null ? cntr.getCntrTare() : BigDecimal.ZERO);
                baseInfo.setGrossWt(baseInfo.getNetWt().add(baseInfo.getCntrTare()));
                String remark = cntr.getCntrSize();
                if ("GP".equals(cntr.getCntrType()) == false) {
                    remark += cntr.getCntrType();
                }
                if (CargoTypeEnum.RF.getValue().equals(fob.getBlnoCargo().getCargoType())) {
                    remark += " " + CargoTypeEnum.RF.getValue() + ":" + fob.getBlnoCargo().getTemperature();
                }
                String checkSpecial = checkSpecialCargo(fob.getBlnoCargo().getCargoType(), fob.getBlnoCargo().getCargoSubType());
                if (StringUtils.isNotEmpty(checkSpecial)) {
                    remark += " " + checkSpecial + ":" + fob.getBlnoCargo().getUnNo() + "/" + fob.getBlnoCargo().getImoClass();
                }
                baseInfo.setRemark(remark);
                baseList.add(baseInfo);
            }
            if (ExImTypeEnum.EX.getValue().equals(exImType)) {
                fobResult.setPodCode(StringUtils.join(portCodeSet, ","));
            } else {
                fobResult.setPolCode(StringUtils.join(portCodeSet, ","));
            }
        }
        List<BlnoNumericalContainerRespDto.BaseInfo> allBaseInfo = new ArrayList<>(16);
        List<BlnoNumericalContainerRespDto.NumericalContainerInfo> numericalContainerList = new ArrayList<>(16);
        for (Map.Entry<String, List<BlnoNumericalContainerRespDto.BaseInfo>> baseInfo : listMap.entrySet()) {
            allBaseInfo.addAll(baseInfo.getValue());
            numericalContainerList.add(dealCount(baseInfo.getKey(), baseInfo.getValue()));
        }
        fobResult.setNumericalContainerList(numericalContainerList);
        fobResult.setCountAllInfo(getCountInfo(allBaseInfo));
        return fobResult;
    }

    public BlnoNumericalContainerRespDto.NumericalContainerInfo dealCount(String portCode, List<BlnoNumericalContainerRespDto.BaseInfo> baseInfos) {
        if (CollectionUtil.isEmpty(baseInfos)) {
            return null;
        }
        BlnoNumericalContainerRespDto.NumericalContainerInfo ncInfo = new BlnoNumericalContainerRespDto.NumericalContainerInfo();
        ncInfo.setPortCode(portCode);
        ncInfo.setInfoList(baseInfos);
        ncInfo.setCountInfo(getCountInfo(baseInfos));
        return ncInfo;
    }

    public BlnoNumericalContainerRespDto.CountInfo getCountInfo(List<BlnoNumericalContainerRespDto.BaseInfo> baseInfos) {
        BlnoNumericalContainerRespDto.CountInfo countInfo = new BlnoNumericalContainerRespDto.CountInfo();
        Set<String> cntrSet = new HashSet<>(16);
        Map<String, BlnoNumericalContainerRespDto.WgtInfo> netWtMap = new HashMap<>(16);
        Map<String, BlnoNumericalContainerRespDto.WgtInfo> grossWtMap = new HashMap<>(16);
        Map<String, Integer> cntrMap = new HashMap<>(16);
        BigDecimal totalNetWt = BigDecimal.ZERO;
        BigDecimal totalGrossWt = BigDecimal.ZERO;
        Long teuCount = 0L;
        for (BlnoNumericalContainerRespDto.BaseInfo info : baseInfos) {
            String key = info.getCntrSize() + "_" + info.getCntrType() + "_" + info.getCntrStatus();
            Integer cntrQuantity = cntrMap.get(key);
            if (cntrQuantity == null) {
                cntrMap.put(key, 1);
            } else {
                if (cntrSet.contains(info.getCntrNo()) == false) {
                    cntrMap.put(key, cntrMap.get(key) + 1);
                }
            }
            String cntrSize = info.getCntrSize();
            BigDecimal netWt = info.getNetWt();
            BigDecimal grossWt = info.getGrossWt();
            BigDecimal cntrTare = info.getCntrTare();
            BlnoNumericalContainerRespDto.WgtInfo netWtInfo = netWtMap.get(cntrSize);
            if (netWtInfo == null) {
                netWtInfo = new BlnoNumericalContainerRespDto.WgtInfo();
                netWtInfo.setCntrSize(cntrSize);
                netWtInfo.setCntrQuantity(1);
                netWtInfo.setWeight(netWt);
                netWtMap.put(cntrSize, netWtInfo);
            } else {
                netWtInfo.setCntrQuantity(netWtInfo.getCntrQuantity() + 1);
                netWtInfo.setWeight(netWtInfo.getWeight().add(netWt));
            }
            //箱号 重复利用
            if (cntrSet.contains(info.getCntrNo())) {
                netWtInfo.setCntrQuantity(netWtInfo.getCntrQuantity() - 1);
            }

            BlnoNumericalContainerRespDto.WgtInfo grossWtInfo = grossWtMap.get(cntrSize);
            if (grossWtInfo == null) {
                grossWtInfo = new BlnoNumericalContainerRespDto.WgtInfo();
                grossWtInfo.setCntrSize(cntrSize);
                grossWtInfo.setCntrQuantity(1);
                grossWtInfo.setWeight(grossWt);
                grossWtMap.put(cntrSize, netWtInfo);
            } else {
                grossWtInfo.setCntrQuantity(netWtInfo.getCntrQuantity() + 1);
                grossWtInfo.setWeight(netWtInfo.getWeight().add(grossWt));
            }
            //箱号 重复利用
            if (cntrSet.contains(info.getCntrNo())) {
                grossWtInfo.setCntrQuantity(grossWtInfo.getCntrQuantity() - 1);
                // 减去皮重
                grossWtInfo.setWeight(grossWtInfo.getWeight().subtract(cntrTare));
            } else {
                teuCount += CntrSizeEnum.size20.getValue().equals(cntrSize) ? 1 : 2;
            }

            totalNetWt = totalNetWt.add(netWtInfo.getWeight());
            totalGrossWt = totalGrossWt.add(grossWtInfo.getWeight());
            //箱号 已经计算过
            cntrSet.add(info.getCntrNo());
        }
        countInfo.setNetWtList(netWtMap.values().stream().collect(Collectors.toList()));
        countInfo.setGrossWtList(grossWtMap.values().stream().collect(Collectors.toList()));
        countInfo.setTotalNetWt(totalNetWt);
        countInfo.setTotalGrossWt(totalGrossWt);
        countInfo.setTeuCount(teuCount);
        List<BlnoNumericalContainerRespDto.CntrInfo> cntrList = new ArrayList<>(16);
        for (Map.Entry<String, Integer> f : cntrMap.entrySet()) {
            String[] v = f.getKey().split("_");
            BlnoNumericalContainerRespDto.CntrInfo cntrInfo = new BlnoNumericalContainerRespDto.CntrInfo();
            cntrInfo.setCntrSizeType(v[0]);
            cntrInfo.setCntrStatus(v[1]);
            cntrInfo.setCntrQuantity(f.getValue());
            cntrList.add(cntrInfo);
        }
        countInfo.setCntrList(cntrList);
        countInfo.setBlnoCount(baseInfos.stream().map(e -> e.getBlNo()).distinct().count());
        return countInfo;
    }
}